﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Windows.Controls.Primitives;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using UmlControlLibrary.AStar;
using System.Reflection;

//TODO:
// génération relations : distance par rapport à la classe
//                        déselectionner la relation avant le routing
//                        ajouter les relations à la map avant routing
// layout graphviz
//
// import/export xmi
// evenements
// analyse saisie regexp
// interface
// enum
// stereotypes
// customization couleurs

namespace UmlControlLibrary
{
    /// <summary>
    /// Interaction logic for UmlDiagramControl.xaml
    /// </summary>
    /// 
    public partial class UmlDiagramControl : SelectableItemControl, IDraggable, IEventListener
    {
        private static ResourceDictionary theme = null;
         private const string ThemeUri = @"PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component\themes/aero.normalcolor.xaml";       
        // private const string ThemeUri = @"PresentationFramework.Luna;V3.0.0.0;31bf3856ad364e35;component\themes/luna.normalcolor.xaml";
        // private const string ThemeUri = @"PresentationFramework.Royale;V3.0.0.0;31bf3856ad364e35;component\themes/royale.normalcolor.xaml";

         public Nullable<Point> InsertionPos = null;

        public ObservableCollection<SelectableItemControl> Childs = new ObservableCollection<SelectableItemControl>();

        private Point workspaceSize = new Point(6144, 4096);
        public Point WorkspaceSize
        {
            get { return workspaceSize; }
            set { workspaceSize = value; }
        }

        
        public Thumb AddThumb(double x, double y)
        {
            Thumb t = new Thumb();
            t.Height = 8;
            t.Width = 8;
            Canvas.SetLeft(t, x - 4);
            Canvas.SetTop(t, y - 4);
            return t;
        }

        static UmlDiagramControl()
        {
            Assembly assembly = typeof(UmlDiagramControl).Assembly;
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                if (type.IsSubclassOf(typeof(UmlDesignerElement)))
                {
                    MethodInfo registerMethod = type.GetMethod("RegisterControl");
                    if (registerMethod != null)
                        registerMethod.Invoke(null, null);
                }
            }
        }

        public UmlDiagramControl():base()
        {
            InitializeComponent();
            if (theme == null)
            {
                Uri uri = new Uri(ThemeUri, UriKind.Relative);
                theme = Application.LoadComponent(uri) as ResourceDictionary;
            }

            EventService.RegisterListener(typeof(EventService),new DebugConsole(this), false);

            this.Resources.MergedDictionaries.Add(theme);
            AddDragZone(rootCanvas);

            UmlModel.CurrentDiagram.Elements.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Classes_CollectionChanged);

            this.ContextMenu = new UmlDiagramControlContextMenu();
            this.ContextMenuOpening += new ContextMenuEventHandler(UmlDiagramControl_ContextMenuOpening);

            UmlTreeView.DataContext = UmlModel.GetInstance();// UmlElementList.Instance;
            UmlTreeView.CanDrag = true;

            EventService.RegisterListener(typeof(UmlRelation), this, true);

            UmlClass _c = new UmlClass("Classe A");
            _c.Locked = true;
            _c.PropertyList.Add(new UmlProperty("property_A1"));
            _c.PropertyList.Add(new UmlProperty("property_A2"));
            _c.PropertyList.Add(new UmlProperty("property_A3"));
            _c.PropertyList.Add(new UmlProperty("property_A4"));
            _c.PropertyList.Add(new UmlProperty("property_A5"));
            _c.PropertyList.Add(new UmlProperty("property_A6"));
            _c.MethodList.Add(new UmlMethod("method_A1","void", UmlModifier._public, new UmlParameter[] {new UmlParameter("Username","String"), new UmlParameter("Password", "String")}));
            _c.MethodList.Add(new UmlMethod("method_A2"));
            _c.MethodList.Add(new UmlMethod("method_A3"));
            _c.MethodList.Add(new UmlMethod("method_A4"));

            _c.UmlElementPropertyChanged += new UmlElementPropertyChangedDelegate(_c_DiagramElementPropertyChanged);

            UmlModel.CurrentDiagram.AddElement(_c);
            //UmlDiagram.Instance.AddElement(_c);

            UmlInterface _i;
            _i = new UmlInterface("Interface B");
            _i.MethodList.Add(new UmlMethod("method B1"));
            _i.MethodList.Add(new UmlMethod("method B2"));
            _i.MethodList.Add(new UmlMethod("method B3"));
            _i.MethodList.Add(new UmlMethod("method B4"));

            UmlModel.CurrentDiagram.AddElement(_i);
        }

        void UmlDiagramControl_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            (this.ContextMenu as UmlDiagramControlContextMenu).SetMenuContexts(e, this, this);
        }

        void _c_DiagramElementPropertyChanged(UmlElementPropertyChangedEventArgs args)
        {
        }

        void Classes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach(UmlElement c in e.NewItems)
                        if (c is DiagramElement)
                            AddNewDiagramElement((DiagramElement)c);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
            }
        }

        public UmlControl AddNewDiagramElement(DiagramElement _element)
        {
            if (InsertionPos == null)
                InsertionPos = new Point((Childs.Count + 1) * 400 + (WorkspaceSize.X / 2), 200 + (WorkspaceSize.Y / 2));

            UmlControl c = new UmlControl();
            c.DataContext = _element;
            c.Template = (ControlTemplate)this.Resources["DraggableAndResizableDesignerItem"];
            c.ApplyTemplate();

            Canvas.SetLeft(c, SelectableItemControl.AlignToGrid(InsertionPos.Value.X));
            Canvas.SetTop(c, SelectableItemControl.AlignToGrid(InsertionPos.Value.Y));

            InsertionPos = null;

            this.AddChild(c);
            UmlModel.CurrentDiagram.AddElementControl(c);

            ControlTemplate ct = (ControlTemplate)c.FindResource("DraggableAndResizableDesignerItem");
            Control Draggable = (Control)ct.FindName("DraggableClassView",c);

            //            ControlTemplate subCt = (ControlTemplate)this.Resources["Drag" + _class.ItemType + "Template"];
            ControlTemplate subCt = _element.GetElementTemplate();
            if (subCt != null)
            {
                Draggable.Template = subCt;
                Draggable.ApplyTemplate();
            }
            return c;
        }

        #region ZOrdering
        private List<SelectableItemControl> ZPosList = new List<SelectableItemControl>();
        private void SetOnTop(SelectableItemControl control)
        {
            if ((control != null) && (control != this))
            {
                if (ZPosList.Contains(control))
                    ZPosList.Remove(control);
                ZPosList.Insert(0, control);
            }
        }

        private void ZRemoveControl(SelectableItemControl control)
        {
            if (ZPosList.Contains(control))
                ZPosList.Remove(control);
        }

        private int GetZOrder(SelectableItemControl control)
        {
            if (ZPosList.Contains(control))
                return ZPosList.IndexOf(control);
            else
                return -1;
        }

        private SelectableItemControl GetTopControl(List<SelectableItemControl> control)
        {
            List<int> ZPos = control.Select(c => GetZOrder(c)).Where(i => i>=0).ToList();
            ZPos.Sort();
            if (ZPos.Count > 0)
                return ZPosList[ZPos[0]];
            else
                return null;
        }

        #endregion

        #region Canvas Size Calculation

        private List<SelectableItemControl> XPosList = new List<SelectableItemControl>();
        private List<SelectableItemControl> YPosList = new List<SelectableItemControl>();

        public void ResizeRootCanvasWidth()
        {
            SelectableItemControl first;
            SelectableItemControl last;

            XPosList.Sort(delegate(SelectableItemControl c1, SelectableItemControl c2)
            { return c1.Left.CompareTo(c2.Left); }
              );
            if (XPosList.Count > 0)
            {
                first = XPosList.First<SelectableItemControl>();
                double min = first.Left;
                XPosList.Sort(delegate(SelectableItemControl c1, SelectableItemControl c2)
                { return c1.Right.CompareTo(c2.Right); }
                  );
                last = XPosList.Last<SelectableItemControl>();
                double max = last.Right;
                Canvas.SetLeft(HilightLayer, min - 50);
                HilightLayer.Width = max + 100 - min;
            }
        }

        public void ResizeRootCanvasHeight()
        {
            SelectableItemControl first;
            SelectableItemControl last;

            YPosList.Sort(delegate(SelectableItemControl c1, SelectableItemControl c2)
            { return c1.Top.CompareTo(c2.Top); }
              );
            if (YPosList.Count > 0)
            {               
                first = YPosList.First<SelectableItemControl>();
                double min = first.Top;
                YPosList.Sort(delegate(SelectableItemControl c1, SelectableItemControl c2)
                { return c1.Bottom.CompareTo(c2.Bottom); }
                  );               
                last = YPosList.Last<SelectableItemControl>();
                double max = last.Bottom; 
                Canvas.SetTop(HilightLayer, min - 50);
                HilightLayer.Height = max + 100 - min;
            }
        }

        public void ResizeRootCanvas()
        {
            ResizeRootCanvasWidth();
            ResizeRootCanvasHeight();
        }

        #endregion

        #region childs changes handling

        internal void ChildUpdated(SelectableItemControl child)
        {
            //SelectableItemControl last;
            //if (child.Right >= rootCanvas.Width + 16)
            //{
            //    rootCanvas.Width = child.Right + 16;
            //    last = XPosList.Last<SelectableItemControl>();
            //    if (last != child)
            //    {
            //        XPosList.Remove(child);
            //        XPosList.Add(child);
            //    }
            //}
            //else

            ResizeRootCanvasWidth();
            
            //if (child.Bottom >= rootCanvas.Height + 16)
            //{
            //    rootCanvas.Height = child.Bottom + 16;
            //    last = YPosList.Last<SelectableItemControl>();
            //    if (last != child)
            //    {
            //        YPosList.Remove(child);
            //        YPosList.Add(child);
            //    }
            //}
            //else
            ResizeRootCanvasHeight();
        }

        SelectableItemControl previousontop = null;
        SelectableItemControl ontop = null;

        public SelectableItemControl GetControlAtPos(Point p)
        {
            List<SelectableItemControl> candidates = new List<SelectableItemControl>();
            foreach (SelectableItemControl child in XPosList)
            {
                if ((p.X > child.Left) && (p.X < child.Right) && (p.Y > child.Top) && (p.Y < child.Bottom))
                  // ask the candidate if we are REALLY above it (margin and all the yada yada)
                    candidates.Add(child);
            }

            previousontop = ontop;
            ontop = GetTopControl(candidates);
            if (previousontop!=ontop)
            {

            }
            return ontop;
        }


        public override void ChildMoved(SelectableItemControl child)
        {
            base.ChildMoved(child);
            ChildUpdated(child);
        }

        public override void ChildResized(SelectableItemControl child)
        {
            base.ChildResized(child);
            ChildUpdated(child);
        }

        public override void Select(SelectableItemControl item)
        {
            SelectableItemControl current = SelectedItem;
            base.Select(item);
            if (SelectedItem != current)
                SetOnTop(SelectedItem.SelectableParent);
        }

/*        public override IAddChild ChildContainer{
            get { return rootCanvas; }
        }*/

        public override void AddChild(object value)
        {
            if (value is SelectableItemControl)
            {
                SelectableItemControl control = (SelectableItemControl)value;
                control.OwnerElement = this;
                rootCanvas.Children.Add(control);
                Childs.Add(control);
                XPosList.Add(control);
                YPosList.Add(control);
                control.CalculateBounds();
                ChildUpdated(control);
                SetOnTop(control);
            }
            else if (value is UIElement)
            {
                rootCanvas.Children.Add((UIElement)value);
            }
            else
                base.AddChild(value);
        }

        public override void RemoveChild(object child)
        {
            if (child is SelectableItemControl)
            {
                SelectableItemControl c = (SelectableItemControl)child;
                if (Childs.Contains(c))
                    Childs.Remove(c);
                ZRemoveControl(c);
            }
            if (child is UIElement)
            {
                UIElement e = (UIElement)child;
                if (rootCanvas.Children.Contains(e))
                    rootCanvas.Children.Remove(e);
            }
        }

        #endregion

        #region Events

        private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ResizeRootCanvas();
            double h = ActualHeight;
            double w = ActualWidth;
            ScrollViewer1.Height = h;
            ScrollViewer1.Width = w;

            debugConsole.Width = w;
            Canvas.SetTop(debugConsole, h - debugConsole.Height);
        }

        private void rootCanvas_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(UmlElement)))
            {
                UmlElement sourceElement = (UmlElement)e.Data.GetData(typeof(UmlElement));

                sourceElement.UmlElementPropertyChanged += new UmlElementPropertyChangedDelegate(_c_DiagramElementPropertyChanged);
                UmlControl c = AddNewDiagramElement(sourceElement as DiagramElement);
                Canvas.SetLeft(c, e.GetPosition(this).X);
                Canvas.SetTop(c, e.GetPosition(this).Y);

                c.UpdateLayout();
                if (c.RenderSize.Height > 400)
                    c.Height = 400;
                if (c.RenderSize.Width > 300)
                    c.Width = 300;
                c.Resized();


                //UmlModel.CurrentDiagram.AddElement(sourceElement);

            }
        }


        #endregion Events

        #region IDraggable Members

        public void Drag(double deltaX, double deltaY)
        {
//            EventService.Log(this,new Event("Drag", typeof(UmlDiagramControl)),"x : " + deltaX.ToString() + ", y : " + deltaY.ToString());
            ScrollViewer1.ScrollToHorizontalOffset(ScrollViewer1.HorizontalOffset - (deltaX * uiZoomSlider.Value));
            ScrollViewer1.ScrollToVerticalOffset(ScrollViewer1.VerticalOffset - (deltaY * uiZoomSlider.Value));
            LastDragPos = new Point(LastDragPos.X - deltaX, LastDragPos.Y- deltaY);
        }

        public override Point GetMousePos(MouseEventArgs e)
        {
            return e.GetPosition(rootCanvas);
        }

        #endregion

        private void button1_Click(object sender, RoutedEventArgs e)
        {
/*            UmlRelation r = new UmlRelation(UmlElementList.Instance.Childs[0], UmlElementList.Instance.Childs[1], UmlRelationType.Aggregate);
            r.End1.Label = "1";
            r.End2.Label = "1..*";
            r.Name = "TestRelation";
            UmlRelationControl rc = new UmlRelationControl(r);

            r.RelationType = UmlRelationType.Composition;
  */          /*UmlRelationControl r = new UmlRelationControl(Childs[0] as UmlControl, Childs[1] as UmlControl, UmlRelationType.Aggregate);
            r.End1.Label = "1";
            r.End2.Label = "1..*";
            r.Label = "TestRelation";
            */
       /*     DraggableLabel lbl = new DraggableLabel();
            lbl.Text = "test de label";

            Canvas.SetLeft(lbl, 500);
            Canvas.SetTop(lbl, 500);
            lbl.OwnerElement = this;
            rootCanvas.Children.Add(lbl);*/

        }       

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            AStarRouteRelation((Childs[0] as UmlControl).Relations[0]);

            //CurrentAStarMap = GetMap();

            //AStar.AStar aStar = new AStar.AStar(CurrentAStarMap);
            //List<AStarNode> Path = aStar.Run();

            //foreach (AStarNode node in Path)
            //    CurrentAStarMap[node.X, node.Y] = AStarNode.NodeStatus.Edge;

            //Path = AStar.AStar.TrimPath(Path);


            //UmlRelation r = new UmlRelation(Childs[0] as UmlControl, Childs[1] as UmlControl, UmlRelationType.Aggregate);
            //r.End1.Label = "1";
            //r.End2.Label = "1..*";
            //r.Label = "TestRelation";

            //PolyLineSegment pls = (r.LineFigure.Segments[0] as PolyLineSegment);
            //for (int i = pls.Points.Count - 2; i >= 1; i--)
            //    pls.Points.RemoveAt(i);

            //foreach (AStarNode node in Path)
            //    pls.Points.Insert(pls.Points.Count - 1, new Point(node.X * AStarNode.NodeSize, node.Y * AStarNode.NodeSize));
            //r.RecalcRelationLink();

            //StreamWriter sw = new StreamWriter("c:\\map.txt");
            //for (int y = 0; y < CurrentAStarMap.GetLength(1); y++)
            //{
            //    for (int x = 0; x < CurrentAStarMap.GetLength(0); x++)
            //    {
            //        sw.Write((byte)CurrentAStarMap[x, y]);
            //    }
            //    sw.WriteLine();
            //}
            //sw.Flush();
            //sw.Close();
        }

        #region AStar

        AStarNode.NodeStatus[,] CurrentAStarMap = null;

        public AStarNode.NodeStatus[,] GetMap()
        {
            int mh = (int)Math.Round(rootCanvas.ActualHeight / AStarNode.NodeSize) + 5;
            int mw = (int)Math.Round(rootCanvas.ActualWidth / AStarNode.NodeSize) + 5;

            AStarNode.NodeStatus[,] map = new AStarNode.NodeStatus[mw, mh];

            foreach (SelectableItemControl control in Childs)
            {
                int l = (int)Math.Round(control.Left / AStarNode.NodeSize) - 1;
                int t = (int)Math.Round(control.Top / AStarNode.NodeSize) - 1;
                int r = (int)Math.Round(control.Right / AStarNode.NodeSize) + 1;
                int b = (int)Math.Round(control.Bottom / AStarNode.NodeSize) + 1;

                for (int y = t; y <= b; y++)
                    for (int x = l; x <= r; x++)
                        map[x, y] = AStarNode.NodeStatus.Block;
            }           
            return map;
        }

        public void AStarRouteRelation(UmlRelationControl r)
        {
            if (CurrentAStarMap == null)
                CurrentAStarMap = GetMap();
            PolyLineSegment pls = r.LineFigure.Segments[0] as PolyLineSegment;
            int sx = (int)Math.Round(pls.Points[0].X / AStarNode.NodeSize);
            int sy = (int)Math.Round(pls.Points[0].Y / AStarNode.NodeSize);
            int ex = (int)Math.Round(pls.Points[pls.Points.Count - 1].X / AStarNode.NodeSize);
            int ey = (int)Math.Round(pls.Points[pls.Points.Count - 1].Y / AStarNode.NodeSize);
            CurrentAStarMap[sx, sy] = AStarNode.NodeStatus.Start;
            CurrentAStarMap[ex, ey] = AStarNode.NodeStatus.Goal;

            AStar.AStar aStar = new AStar.AStar(CurrentAStarMap);
            List<AStarNode> Path = aStar.Run();

            CurrentAStarMap[sx, sy] = AStarNode.NodeStatus.Free;
            CurrentAStarMap[ex, ey] = AStarNode.NodeStatus.Free;
            
            /*    foreach (AStarNode node in Path)
                    CurrentAStarMap[node.X, node.Y] = AStarNode.NodeStatus.Edge;*/

            Path = AStar.AStar.TrimPath(Path);
           
            for (int i = pls.Points.Count - 2; i >= 1; i--)
                pls.Points.RemoveAt(i);

            foreach (AStarNode node in Path)
                pls.Points.Insert(pls.Points.Count - 1, new Point(node.X * AStarNode.NodeSize, node.Y * AStarNode.NodeSize));
            r.RecalcRelationLink();
        }

        #endregion

        private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            uiZoomSlider.Value += e.Delta * 0.001;
        }

        #region IEventListener Members

        public void HandleEvent(EventServiceArgs args)
        {
            if (args is EventServiceValidationArgs)
            {
                //if (((EventServiceValidationArgs)args).Event
            }
            else
            {
                // If an UmlRelation creation event was posted
                if (args.Event == UmlRelation.Events.Created)
                {
                    // get the UmlRelation related to the event
                    UmlRelation r = (UmlRelation)args.Sender;
                    // Get the end element
                    UmlElement end1 = r.End1.EndElement;
                    // if the end element is an UmlProperty
                    if (end1 is UmlProperty)
                        // Get the owner control
                        end1 = ((UmlProperty)end1).Owner;

                    // If the diagram contains controls for the relation ends
                    if ((end1 != null)
                        && (r.End2.EndElement != null)
                        && (UmlModel.CurrentDiagram.DiagramControls.ContainsKey(end1))
                        && (UmlModel.CurrentDiagram.DiagramControls.ContainsKey(r.End2.EndElement)))
                    {
                        // Get relations (1 in fact) for these ends
                        IEnumerable<UmlElement> elements = UmlModel.CurrentDiagram.GetRelationsFor(r.End1.EndElement, r.End2.EndElement);
                        // If a relation exists (and it now should, or the event was miscalled)
                        if (elements.Count() > 0)
                        {
                            // if there is not a control for the relation in the diagram
                            if (!UmlModel.CurrentDiagram.DiagramControls.ContainsKey(elements.First()))
                            {
                                // Create the relation control
                                UmlBaseControl control = new UmlRelationControl(r);
                                // Add the control to the control list
                                UmlModel.CurrentDiagram.AddElementControl(control);
                            }
                        }
                    }
                }
            }
        }

        #endregion

        private void recalcTile()
        {
            double offsety = 0;
            double offsetx = 0;

            if (ScrollViewer1.ExtentWidth > ScrollViewer1.ViewportWidth)
            {
                offsetx = - ScrollViewer1.HorizontalOffset;// *uiZoomSlider.Value;
                offsety = - ScrollViewer1.VerticalOffset;// *uiZoomSlider.Value;
            }
            else
            {
                offsetx = (ScrollViewer1.ViewportWidth - ScrollViewer1.ExtentWidth) / 2;
                offsety = (ScrollViewer1.ViewportHeight - ScrollViewer1.ExtentHeight) / 2;
            }

            
            double size = uiZoomSlider.Value * 40;
            rootbackground.Viewport = new Rect(offsetx, offsety, size, size);
        }

        private void uiZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double cx = (ScrollViewer1.HorizontalOffset + (ScrollViewer1.ViewportWidth / 2)) / ScrollViewer1.ExtentWidth;
            if (double.IsNaN(cx)) cx = 0;
            double cy = (ScrollViewer1.VerticalOffset + (ScrollViewer1.ViewportHeight / 2)) / ScrollViewer1.ExtentHeight;
            if (double.IsNaN(cy)) cy = 0;
            uiZoomTransform.ScaleX = e.NewValue;
            uiZoomTransform.ScaleY = e.NewValue;
            ScrollViewer1.UpdateLayout();
            cx = cx * ScrollViewer1.ExtentWidth - (ScrollViewer1.ViewportWidth / 2);
            cy = cy * ScrollViewer1.ExtentHeight - (ScrollViewer1.ViewportHeight / 2);
            if (!double.IsNaN(cx)) ScrollViewer1.ScrollToHorizontalOffset(cx);
            if (!double.IsNaN(cy)) ScrollViewer1.ScrollToVerticalOffset(cy);

    //        recalcTile();
        }

        private void PART_VerticalScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
    //        recalcTile();
        }
    }

    public class UmlLine
    {
        public UmlControl end1
        {
            get;
            set;
        }
        public UmlControl end2
        {
            get;
            set;
        }

        public double X1
        {
            get{return(Canvas.GetLeft(end1) + end1.ActualWidth / 2);}
        }
        public double Y1
        {
            get { return (Canvas.GetTop(end1) + end1.ActualHeight / 2); }
        }
        public double X2
        {
            get { return (Canvas.GetLeft(end2) + end2.ActualWidth / 2); }
        }
        public double Y2
        {
            get { return (Canvas.GetTop(end2) + end2.ActualHeight / 2); }
        }

        private PathGeometry pg = null;
        private Point p1 = new Point();
        private Point p2 = new Point();

        public System.Windows.Shapes.Path Path
        {
            get;
            set;
        }

        public PathGeometry PathGeometry
        {
            get
            {
                if (pg == null)
                {
                    pg = new PathGeometry();
                    PathFigure pf = new PathFigure();
                    p1.X = X1; p1.Y = Y1;
                    p2.X = X2; p2.Y = Y2;
                    pf.StartPoint = p1;
                    pf.Segments.Add(new LineSegment(p2, true));
                    pg.Figures.Add(pf);
                }
                else
                {
                    //PathFigure pf = pg.Figures[0];
                    //p1.X = X1; p1.Y = Y1;
                    //p2.X = X2; p2.Y = Y2;
                    //pf.StartPoint = p1;
                    //int i = pf.Segments.Count-1;
                    //LineSegment ls = (LineSegment)pf.Segments[i];
                    //ls.Point = p2;
                }
                return pg;
            }
            set
            {
                pg = value;
            }
        }

    }

    public class UmlBaseControl : SelectableItemControl
    {
    }

    public class UmlControl: UmlBaseControl, IDraggable
    {
        public double bottom;
        public double right;

       public List<UmlRelationControl> Relations = new List<UmlRelationControl>();

       // public UmlDiagramControl OwnerDiagram;

        public bool Locked
        {
            get;
            set;
        }

        public UmlControl():base()
        {
            this.SizeChanged += new SizeChangedEventHandler(MyUmlControl_SizeChanged);
        }

        void  MyUmlControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
 	        //Moved(0,0);
        }


        public void MoveEnd()
        {
            //Moved(0,0);
 //           OwnerDiagram.UmlControlUpdated(this);
        }

        private double getMagnitude(Point l1, Point l2)
        {
            double x, y;
            x = l2.X - l1.X; x *= x;
            y = l2.Y - l1.Y; y *= y;
            return x + y;
        }

        private double DistanceFromLine(Point p, Point l1, Point l2)
        {
            const double EPS = 0.000001;
            const double EPSEPS = EPS * EPS;
            double SqLineMag,u,ix,iy;
            double result;
            
            SqLineMag = getMagnitude(l1,l2);
            if (SqLineMag < EPSEPS)
                return -1;
 
            u = ((p.X-l1.X)*(l2.X-l1.X) + (p.Y-l1.Y)*(l2.Y-l1.Y)) / SqLineMag;           
            if ((u < EPS) || (u > 1))
            {
               ix = getMagnitude(p,l1);
               iy = getMagnitude(p,l2);
               result = Math.Min(ix,iy);   
            }
            else
            {
               ix = l1.X + u * (l2.X - l1.X);
               iy = l1.Y + u * (l2.Y - l1.Y);
               result = getMagnitude(p, new Point(ix, iy));
            }
            return Math.Sqrt(result);
        }

        public Point MagnetPoint(Point p)
        {
            LineHelper.Side side;
            return MagnetPoint(p, out side);
        }

        public Point MagnetPoint(Point p, out LineHelper.Side side)
        {
            double dt, db, dl, dr, d;
            double t = Canvas.GetTop(this)+7.5,
                   b = Canvas.GetBottom(this)-7.5,
                   l = Canvas.GetLeft(this)+7.5,
                   r = Canvas.GetRight(this)-7.5;

            if (double.IsNaN(b))
            {
                b = t + this.ActualHeight - 15;
                r = l + this.ActualWidth - 15;
            }

            double x = p.X, y = p.Y;
            side = LineHelper.Side.Top;

            if (x > r)
            {
                x = r;
                side = LineHelper.Side.Right;
            }
            if (x < l)
            {
                x = l;
                side = LineHelper.Side.Left;
            }
            if (y > b)
            {
                y = b;
                side = LineHelper.Side.Bottom;
            }
            if (y < t)
            {
                y = t;
                side = LineHelper.Side.Top;
            }



            return new Point(x, y);
        }

        public override void Resized()
        {
            base.Resized();
            foreach (UmlRelationControl relation in Relations)
            {
                relation.RecalcRelationLink(this);
           /*     Point p;
                if (relation.End1.EndControl == this)
                    p = relation.LineFigure.StartPoint;
                else
                {
                    int n = (relation.LineFigure.Segments[0] as PolyLineSegment).Points.Count - 1;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n];
                }
                Point p2 = this.MagnetPoint(p);
                Drag(p2.X - p.X, p2.Y - p.Y);*/
            }
        }

        #region IDraggable Members

        public void Drag(double deltaX, double deltaY)
        {
            Point p, p2,p3;
            foreach (UmlRelationControl relation in Relations)
            {
                relation.RecalcRelationLink(this);
            /*    if (relation.End1.EndControl == this)
                {
                    
                    p = relation.LineFigure.StartPoint;
                    p.Offset(deltaX, deltaY);
                    relation.LineFigure.StartPoint = p;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[0];
                    p2 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[1];
                    p3 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[2];
                    if (LineHelper.GetDirection(relation.End1.Side) == LineHelper.LineDirection.Horizontal)
                    {
                        p.Offset(0, deltaY);
                        if (p2.X == p3.X)
                            p2 = new Point(p2.X, (p.Y + p3.Y) / 2);
                    }
                    else
                    {
                        p.Offset(deltaX, 0);
                        if (p2.Y == p3.Y)
                            p2 = new Point((p.X + p3.X) / 2, p2.Y);                       
                    }
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[0] = p;
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[1] = p2;
                }
                else
                {
                    int n = (relation.LineFigure.Segments[0] as PolyLineSegment).Points.Count - 1;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n];
                    p.Offset(deltaX, deltaY);
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n] = p;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 1];
                    p2 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 2];
                    p3 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 3];
                    if (LineHelper.GetDirection(relation.End2.Side) == LineHelper.LineDirection.Horizontal)
                    {
                        p.Offset(0, deltaY);
                        if (p2.X == p3.X)
                            p2 = new Point(p2.X, (p.Y + p3.Y) / 2);
                    }
                    else
                    {
                        p.Offset(deltaX, 0);
                        if (p2.Y == p3.Y)
                            p2 = new Point((p.X + p3.X) / 2, p2.Y);
                    }
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 1] = p;
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 2] = p2;
                }*/
            }
        }

        #endregion
    }

    public class DebugConsole:IEventListener
    {
        UmlDiagramControl Owner;
        public DebugConsole(UmlDiagramControl owner)
        {
            Owner = owner;
        }

        #region IEventListener Members

        public void HandleEvent(EventServiceArgs args)
        {
            Owner.debugConsole.Text += ((EventService.LogEvent)args.Datas).ToString() + "\r\n";
        }

        #endregion
    }
}
